Someone asked on the PyGTK mailinglist if it is possible to reuse certain parts of a libglade window several times within an application.
It certainly is, and it's not even hard to do!
I've quickly written a demo application to show how it's done.
The user interface designed in glade is really simple, a textbox with two toggle buttons underneath. Activating one button will display a second, disparate textbox beside the original one. Deactivating it will remove that second textarea.
The other button will display and remove a copy of the first textbox which was there from application startup.
The full widget tree of this interface is shown in
the first screenshot.
The actual code to the demo application is rather brief.
The main point to keep in mind is that you can't actually reuse the same widget, more than once, at one time. You could reuse it if you'd make sure it's never used more than once at the same time, and keep track of it (you also have to make certain it doesn't get accidentally destroyed along the way!)
What the demo really does, and what libglade allows you to do, is use (parts of) the same interface definition over and over again to build
distinct PyGTK objects and add them to the existing interface. At least the PyGTK implementation of libglade, and possibly the native C libglade as well, actually caches the interface definition in case you'd want to reuse parts of it for additional object creation.
Let's have a closer look at how it's done:
import gtk, gtk.glade
class ReuseDemo:
def __init__ (self):
self.gladefile = 'glade-reuse-demo.glade'
self.xml = gtk.glade.XML (self.gladefile)
self.win = self.xml.get_widget ('firstWindow')
self.theContainer = self.xml.get_widget ('theHBox')
self.xml.signal_autoconnect (
dict (gtk_main_quit=gtk.main_quit,
btn1_toggled=self.toggle1,
btn2_toggled=self.toggle2))
self.buttons = (self.xml.get_widget ('tbtn1'), self.xml.get_widget ('tbtn2'))
self.childTree = None
self.win.show_all()
First step is to import the gtk and gtk.glade modules.
The actual demo is in a little class; it's constructor starts by storing the name of the glade file, because it's needed later to construct the additional objects from (even if the caching should mean it isn't really re-read anymore). The xml tree from this file is parsed by pyglade and the window object is retrieved from the first of the two windows defined in it. At this point the window is already a PyGTK object!
The container that needs to have objects added to and removed from is also retrieved and stored as a class attribute. The signals for the two toggle buttons and to allow a graceful exit by means of the windowmanager close button are connected to their handlers, and the two togglebuttons' objects are also retrieved and stored away for later reference. Finally, a variable to hold a reference to the secondary textbox objects is initiated, and the window is shown.
def toggle1 (self, btn):
self.toggle (btn, 'txtContent', 1)
def toggle2 (self, btn):
self.toggle (btn, 'txtOther', 2)
The two toggle button callbacks call a single method, with the gladeXML tagname for the respective textbox object each one needs to display or remove and an index number for arguments (in addition to the togglebutton object reference that the callback received as it's first argument).
def toggle (self, btn, tagname, idx):
self.buttons[(idx%2)].set_sensitive (not btn.get_active())
if not btn.get_active():
if self.childTree:
self.theContainer.remove (self.childTree)
self.childTree = None
else:
objXML = gtk.glade.XML (self.gladefile, root=tagname)
self.childTree = objXML.get_widget (tagname)
self.theContainer.pack_end (self.childTree)
The important logic is in the final method. First, the button which is
not toggled is disabled or enabled for user interaction, depending on the activated state of the toggled button. Because the indices passed on by the two toggle buttons' callbacks were one-based, while the python tuple that holds references to the toggle buttons is zero-based, the remainder of a division by two of the callback index will automatically point to the button that was not toggled. Alternatively the values 1 and 0, in that order, and thus pointing directly to the tuple indices could also have been used as index arguments for the two callbacks.
The necessity of disabling the other button when one is activated, and therefore two textboxes are displayed, is because the demo isn't written to support displaying more than two textboxes at the same time, but especially removing the correct textbox when three would be visible. If it were extended in that sense, the need to disable the other button would disappear.
Next the activated state is tested; if the button is deactivated, the second textbox is removed from the container and the reference to it removed, while if it's activated, a new object is instantiated from the gladeXML description and added to the container.
Here's where the gladeXML tagnames come in: to recreate only part of the widget tree, and retrieve the actual widget from the parsed tree. Note that you must reconstruct the parse tree here; retrieving the same object from the original parsetree and trying to add it a second time to the container will result in a GTK+ warning (on the console) and nothing being added at all.
That's all there is to it, really. All that is left is to instantiate a class instance and start the GTK+ mainloop:
if __name__ == '__main__':
ReuseDemo()
gtk.main()
The second screenshot is an example of how the application might look in one specific state.
A tarball with the glade and python code is available from the
sysfs download area.